#!/usr/bin/env bash

include "objectv5.sh"
include "databasev2.sh"
include "test.sh"

ObjectV5::DefineClass DatabaseV2Binding "
    --cargs=db,target_class,target_this
    --string=db
    --string=target_class
    --string=target_this
"

function DatabaseV2Binding::DatabaseV2Binding()
{
    local this="$1"; shift
    local db="$(DatabaseV2Binding::db "$this")"; shift
    local target_class="$(DatabaseV2Binding::target_class "$this")"; shift
    local target_this="$(DatabaseV2Binding::target_this "$this")"; shift

    # "$target_class::New" "$target_this" "$@"
    for key in $(DatabaseV2::GetKeys "$db"); do
        if ! DatabaseV2::IsObject "$db" "$key"; then
            "$target_class::$key" "$target_this" "$(DatabaseV2::GetVal "$db" $key)"
            continue
        fi

        if DatabaseV2::IsArray "$db" "$key"; then
            for index in $(DatabaseV2::GetKeys "$db" "$key"); do
                "$target_class::$key" "$target_this" push "$(DatabaseV2::GetVal "$db" "$key" "$index")"
            done
            continue
        fi

        assert_fail "is object"
    done

    eval '
        function '$target_class'::DatabaseV2Binding::OnVal()
        {
            local typename="$1"; shift
            local keyname="$1"; shift
            local this="$1"; shift
            # string
            if [ "$typename" == "string" ] && (( $# == 1 )); then

                local val="$1"; shift
                DatabaseV2::SetVal '$db' "$keyname" "$val"

            # integer
            elif [ "$typename" == "integer" ] && (( $# == 1 )); then

                if [ $1 == inc ]; then
                    DatabaseV2::SetVal '$db' "$keyname" $(($('$target_class'::$keyname '$target_this') + 1))
                elif [ $1 == dec ]; then
                    DatabaseV2::SetVal '$db' "$keyname" $(($('$target_class'::$keyname '$target_this') - 1))
                else
                    local val="$1"; shift
                    DatabaseV2::SetVal '$db' "$keyname" "$val"
                fi

            # array
            elif [ "$typename" == "array" ] && (( $# == 2 )); then

                if [ $1 == push ]; then
                    local val="$2"
                    DatabaseV2::SetVal '$db' "$keyname" "$('$target_class'::$keyname '$target_this' len)" "$val"
                elif [ $1 == erasekey ]; then
                    local key="$2"
                    DatabaseV2::EraseKey '$db' "$keyname" "$key"
                elif [ $1 == eraseval ]; then
                    local val="$2"
                    DatabaseV2::EraseVal '$db' "$keyname" "$val"
                fi

            fi
            return 0
        }
    '
    $target_class::AddProxy $target_this $target_class::DatabaseV2Binding::OnVal
    return
}

function DatabaseV2Binding::~DatabaseV2Binding()
{
    local this="$1"; shift
    local target_class="$(DatabaseV2Binding::target_class "$this")"
    local target_this="$(DatabaseV2Binding::target_this "$this")"
    $target_class::DelProxy "$target_this" $target_class::DatabaseV2Binding::OnVal
    return
}

function DatabaseV2Binding::Test1()
{
    ObjectV5::DefineClass GlobalTable "
        --proxy
        --string=version
        --array=repos
    "

    local testpath="$(test::get_temp_path "databasev2_binding.test1")"
    DatabaseV2 testdb{"$testpath"}
    GlobalTable gt
    DatabaseV2Binding::New binding{testdb, GlobalTable, gt}

    # binding object is empty
    test::run_cmd GlobalTable::version gt
    test::should_be ""
    GlobalTable::version gt 15
    test::run_cmd GlobalTable::version gt
    test::should_be 15

                  GlobalTable::repos gt push ohosm1
                  GlobalTable::repos gt push ohosm2
    test::run_cmd GlobalTable::repos gt len
    test::should_be 2

    test::run_cmd GlobalTable::repos gt 0
    test::should_be ohosm1

    test::run_cmd GlobalTable::repos gt all
    test::should_be "ohosm1 ohosm2"

    GlobalTable::repos gt eraseval ohosm1

    # remove binding
    DatabaseV2Binding::Del binding

    # memory object version is 25
    GlobalTable::version gt 25
    test::run_cmd GlobalTable::version gt
    test::should_be 25

    # binding object version is 15
    GlobalTable gt2
    DatabaseV2Binding::New binding{testdb, GlobalTable, gt2}
    test::run_cmd GlobalTable::version gt2
    test::should_be 15

    DatabaseV2Binding::Del binding

    DatabaseV2::destroy testdb
    rm -rf "$testpath"
    return
}
test::register_test DatabaseV2Binding::Test1

function DatabaseV2Binding::Test2()
{
    ObjectV5::DefineClass Object "
        --proxy
        --cargs=str
        --string=str
    "

    local testpath="$(test::get_temp_path "databasev2_binding.test2")"
    DatabaseV2 testdb{"$testpath"}
    Object obj{abcdefg}
    DatabaseV2Binding binding{testdb, Object, obj}

    test::run_cmd Object::str obj
    test::should_be abcdefg

    Object::Del obj
    DatabaseV2::destroy testdb
    rm -rf "$testpath"
    return
}
test::register_test DatabaseV2Binding::Test2
